/*
* Copyright (C) 2019 ~ 2019 Deepin Technology Co., Ltd.
*
* Maintainer: <changze@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QFileInfo>
#include <QtWidgets>
#include "cplusplus/CppDocument.h"
#include "cplusplus/TranslationUnit.h"
#include "CppRewriter.h"
#include "cplusplus/AST.h"
#include "cplusplus/Parser.h"
#include "cplusplus/Bind.h"
#include <QStringList>
#include <cplusplus/ASTVisitor.h>
#include <QHeaderView>

using namespace CPlusPlus;

static QString symbolName(Symbol *symbol)
{
    if(nullptr == symbol)
        return "";
    const Identifier *objectId = symbol->identifier();
    if(nullptr == objectId)
        return "";
    return QByteArray(objectId->chars(), static_cast<int>(objectId->size()));
}



MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    model = new QStandardItemModel(this);
    model->setHorizontalHeaderLabels(QStringList() << QStringList() << "ClassName" << "FuncName" << "EXPECT COUNT");
    ui->tableView->setModel(model);
    ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->tableView->resizeColumnsToContents();
    ui->tableView->setSortingEnabled(true);

    resize(1100, 600);
}

MainWindow::~MainWindow()
{
    delete ui;
}


QList<FunctionInfo> MainWindow::getFuncInfo(const QString &filepath)
{
    QList<FunctionInfo> list;
    //判断是否为文件
    if(!QFileInfo(filepath).isFile()) {
        return list;
    }

    QFile file(filepath);
    file.open(QFile::ReadOnly);
    QByteArray all = file.readAll();

    Document::Ptr doc = Document::create(filepath);
    //导入数据
    doc->setUtf8Source(all);
    //解析
    doc->parse();
    doc->check();
    //解析指针
    ASTVisitor visitor(doc->translationUnit());

    //遍历所有的symbol
    for(uint k = 0, count = doc->globalSymbolCount(); k < count; k ++)
    {
        //symbol 是函数时
        if(doc->globalSymbolAt(k)->isFunction()) {
            list.append(FunctionInfo());
            //获取函数体信息
            Function *func = doc->globalSymbolAt(k)->asFunction();
            //获取函数名
            list.last().funcName = symbolName(doc->globalSymbolAt(k));
            //获取函数参数
            for(uint k = 0, count = func->argumentCount(); k < count; k ++)
            {
                list.last().argNameList << symbolName(func->argumentAt(k));
            }
            //获取函数开始行和结束行
            uint line_start = 0;
            uint line_end = 0;
            visitor.getPosition(func->startOffset(), &line_start);
            visitor.getPosition(func->endOffset(), &line_end);
            list.last().start = line_start;
            list.last().end = line_end;
        }
    }
    return list;
}

void MainWindow::fillModelByFile(const QString &filepath)
{
    qApp->processEvents();
    ui->statusBar->showMessage(QString() + __FUNCTION__ + ":" + filepath);

    QList<FunctionInfo> list = getFuncInfo(filepath);
    for(auto info : list) {
        if(info.funcName.trimmed().isEmpty())
            continue;
        bool ok = false;
        QString funcName = testFuncName(filepath, info, ok);
        if(!ok)
            continue;
        QString className = QFileInfo(filepath).baseName();
        QString expect_count = QString::number(testFuncBody(filepath, info));
        caseCount ++;
        caseExpect += expect_count.toInt() > 0 ? 1 : 0;

        model->appendRow(QList<QStandardItem*>()
                         << new QStandardItem(className)
                         << new QStandardItem(funcName)
                         << new QStandardItem(expect_count)
                         );
    }
}

void MainWindow::fillModel(const QString &dir)
{
    caseCount = 0;
    caseExpect = 0;

    model->removeRows(0, model->rowCount());
    fillModelByDir(dir);

    ui->tableView->resizeColumnsToContents();
    ui->label_message->setText(QString("caseVoid:%1 caseExpect: %2 caseCount:%3")
                               .arg(caseCount - caseExpect)
                               .arg(caseExpect)
                               .arg(caseCount));
    ui->tableView->sortByColumn(2, Qt::AscendingOrder);
//    QMessageBox::information(this, "", "success");

}

void MainWindow::fillModelByDir(const QString &dir)
{
    qApp->processEvents();
    ui->statusBar->showMessage(QString() + __FUNCTION__ + ":" + dir);

    foreach(const QFileInfo &info, QDir(dir).entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, QDir::DirsFirst)) {
        if(info.isFile()) {
            fillModelByFile(info.filePath());
        }
        if(info.isDir()) {
            fillModelByDir(info.filePath());
        }
    }
}

QString MainWindow::testFuncName(const QString &filePath, const FunctionInfo &info, bool &ok)
{
    QFile file(filePath);
    file.open(QFile::ReadOnly);
    int count = info.start;
    while(count --) {
        file.readLine();
    }
    QString line = file.readLine();

    ok = line.contains("TEST_F");

    int begin = line.indexOf(",");
    int end = line.indexOf(")");
    return line.mid(begin + 1, end - begin - 1).trimmed();
}


int MainWindow::testFuncBody(const QString &filePath, const FunctionInfo &info)
{
    QFile file(filePath);
    file.open(QFile::ReadOnly);
    int count = info.start;
    while(count --) {
        file.readLine();
    }
    QString body;
    for(count = info.start; count < info.end; count ++) {
        body += file.readLine();
    }

    return body.count("EXPECT")/* + body.count("ASSERT")*/;
}

void MainWindow::on_pushButton_openFolder_clicked()
{
    QSettings conf("conf.ini");
    QString dirName = QFileDialog::getExistingDirectory(this, "", conf.value("global/path", "~").toString());
    if(dirName.isEmpty())
        return;
    conf.setValue("global/path", dirName);

    fillModel(dirName);
}

void MainWindow::on_pushButton_exit_clicked()
{
    qApp->quit();
}


void MainWindow::on_pushButton_refresh_clicked()
{
    QSettings conf("conf.ini");
    fillModel(conf.value("global/path", "~").toString());
}
